﻿<h1>Go代码断行规则</h1>

<p>
如果你已经写了一些Go代码，你应该知道，Go代码风格不能太随意。
具体说来，我们不能随意在某个空格或者符号字符处断行。
本文余下的部分将列出Go代码中的详细断行规则。
</p>

<h3>分号插入规则</h3>

<div>

我们在Go编程中常遵循的一个规则是：一个显式代码块的起始左大括号<code>{</code>不放在下一行。
比如，下面这个<code>for</code>循环代码块编译将失败。

<pre class="line-numbers"><code class="language-go">	for i := 5; i > 0; i--
	{ // error: 未预料到的新行
	}
</code></pre>

为了让上面这个<code>for</code>循环代码块编译成功，我们不能在起始左大括号<code>{</code>前断行，而应该像下面这样进行修改：

<pre class="line-numbers"><code class="language-go">	for i := 5; i > 0; i-- {
	}
</code></pre>

<p>
</p>

然而，有时候起始左大括号<code>{</code>却可以放在一个新行上，比如下面这个<code>for</code>循环代编译时没有问题的。

<pre class="line-numbers"><code class="language-go">	for
	{
		// do something ...
	}
</code></pre>

<p>
</p>

<p>
那么，Go代码中的根本性换行规则究竟是如何定义的呢？
在回答这个问题之前，我们应该知道一个事实：正式的Go语法是使用（英文）分号<code>;</code>做为结尾标识符的。
但是，我们很少在Go代码中使用和看到分号。为什么呢？原因是大多数分号都是可选的，因此它们常常被省略。
在编译时刻，Go编译器会自动插入这些省略的分号。
</p>

比如，下面这个程序中的十个分号都是可以被省略掉的。

<pre class="line-numbers"><code class="language-go">package main;

import "fmt";

func main() {
	var (
		i   int;
		sum int;
	);
	for i < 6 {
		sum += i;
		i++;
	};
	fmt.Println(sum);
};
</code></pre>

<p>
假设上面这个程序存储在一个<code>semicolons.go</code>文件中，我们可以运行<code>go fmt semicolons.go</code>将此程序中的不必要的分号去除掉。
在编译时刻，编译器会自动此插入这些去除掉的分号（至此文件的内存中的版本）。
</p>

自动插入分号的规则是什么呢？Go白皮书<a href="https://golang.google.cn/ref/spec#Semicolons">这样描述</a>：

<div class="alert alert-success">


<ol>
<li>
	在Go代码中，注释除外，如果一个代码行的最后一个语法词段（token）为下列所示之一，则一个分号将自动插入在此字段后（即行尾）：
	<ul>
	<li>
		一个<a href="keywords-and-identifiers.html#identifier">标识符</a>；
	</li>
	<li>
		一个整数、浮点数、虚部、码点或者字符串<a href="basic-types-and-value-literals.html#basic-value-literals">字面量</a>；
	</li>
	<li>
		这几个跳转关键字之一：<code>break</code>、<code>continue</code>、<code>fallthrough</code>和<code>return</code>；
	</li>
	<li>
		自增运算符<code>++</code>或者自减运算符<code>--</code>；
	</li>
	<li>
		一个右括号：<code>)</code>、<code>]</code>或<code>}</code>。
	</li>
	</ul>
</li>
<li>
	为了允许一条复杂语句完全显示在一个代码行中，分号可能被插入在一个右小括号<code>)</code>或者右大括号<code>}</code>之前。
</li>
</ol>
</div>

<p>
对于上述第一条规则描述的情形，我们当然也可以手动插入这些分号，就像此前的例子中所示。换句话说，这些分号在编程时是可选的。
</p>

上述第二条规则允许我们写出如下的代码：

<pre class="line-numbers"><code class="language-go">import (_ "math"; "fmt")
var (a int; b string)
const (M = iota; N)
type (MyInt int; T struct{x bool; y int32})
type I interface{m1(int) int; m2() string}
func f() {print("a"); panic(nil)}
</code></pre>

编译器在编译时刻将自动插入所需的分号，如下所示：

<pre class="line-numbers"><code class="language-go">var (a int; b string;);
const (M = iota; N;);
type (MyInt int; T struct{x bool; y int32;};);
type I interface{m1(int) int; m2() string;};
func f() {print("a"); panic(nil);};
</code></pre>

<p>
</p>

编译器不会为其它任何情形插入分号。如果其它任何情形需要一个分号，我们必须手动插入此分号。
比如，上例中的每行中的第一个分号必须手动插入。下例中的分号也都需要手动插入。

<pre class="line-numbers"><code class="language-go">var a = 1; var b = true
a++; b = !b
print(a); print(b)
</code></pre>

<p>
</p>

<p>
从以上两条规则可以看出，一个分号永远不会插入在<code>for</code>关键字后，这就是为什么上面的裸<code>for</code>循环例子是合法的。
</p>

分号自动插入规则导致的一个结果是：自增和自减运算必须呈现为单独的语句，它们不能被当作表达式使用。
比如，下面的代码是编译不通过的：

<pre class="line-numbers"><code class="language-go">func f() {
	a := 0
	println(a++)
	println(a--)
}
</code></pre>

上面代码编译不通过的原因是它等价于下面的代码：

<pre class="line-numbers"><code class="language-go">func f() {
	a := 0
	println(a++;)
	println(a--;)
}
</code></pre>

<p>
</p>

分号自动插入规则导致的另一个结果是：我们不能在选择器中的句点<code>.</code>之前断行。
在选择器中的句点之后断行是允许的，比如：

<pre class="line-numbers"><code class="language-go">	anObject.
		MethodA().
		MethodB().
		MethodC()
</code></pre>

而下面这样是非法的：

<pre class="line-numbers"><code class="language-go">	anObject
		.MethodA()
		.MethodB()
		.MethodC()
</code></pre>

此代码片段是非法的原因是编译器将自动在每个右小括号<code>)</code>后插入一个分号，如下面所示：

<pre class="line-numbers"><code class="language-go">	anObject;
		.MethodA();
		.MethodB();
		.MethodC();
</code></pre>

<p>
</p>

<!--
<p>
There are many other inappropriate line break cases.
If you think the above line break rules
are some complex to grasp, here is simpler rule to follow.
The simpler rule is a subset of the full rules.
</p>

<div class="alert alert-success">
Generally, we should only break a code line just after a binary operator,
an assignment sign, a single dot (<code>.</code>), a comma, a simicolon,
or any opening brace (<code>{</code>, <code>[</code>, <code>(</code>).
</div>

<p>
</p>
-->

上述分号自动插入规则可以让我们写出更简洁的代码，同时也允许我们写出一些合法的但看上去有些怪异的代码，比如：

<pre class="line-numbers must-line-numbers"><code class="language-go">package main

import "fmt"

func alwaysFalse() bool {return false}

func main() {
	for
	i := 0
	i < 6
	i++ {
		// 使用i ...
	}

	if x := alwaysFalse()
	!x {
		// ...
	}

	switch alwaysFalse()
	{
	case true: fmt.Println("true")
	case false: fmt.Println("false")
	}
}
</code></pre>

<p>
上例中所有的流程控制代码块都是合法的。编译器将在这些行的行尾自动插入一个分号：第<i>9</i>行、第<i>10</i>行、第<i>15</i>行和第<i>20</i>行。
</p>

注意，上例中的<code>switch-case</code>代码块将输出<code>true</code>，而不是<code>false</code>。
此代码块和下面这个是不同的：

<pre class="line-numbers"><code class="language-go">	switch alwaysFalse() {
	case true: fmt.Println("true")
	case false: fmt.Println("false")
	}
</code></pre>

如果我们使用<code>go fmt</code>命令格式化前者，一个分号将自动添加到<code>alwaysFalse()</code>函数调用之后，如下所示：

<pre class="line-numbers"><code class="language-go">	switch alwaysFalse();
	{
	case true: fmt.Println("true")
	case false: fmt.Println("false")
	}
</code></pre>

插入此分号后，此代码块将和下者等价：

<pre class="line-numbers"><code class="language-go">	switch alwaysFalse(); true {
	case true: fmt.Println("true")
	case false: fmt.Println("false")
	}
</code></pre>

<p>
这就是它输出<code>true</code>的原因。
</p>

<p>
常使用<code>go fmt</code>和<code>go vet</code>命令来格式化和发现可能的逻辑错误是一个好习惯。
</p>

下面是一个很少见的情形，此情形中所示的代码看上去是合法的，但是实际上是编译不通过的。

<pre class="line-numbers"><code class="language-go">func f() {
	switch x {
	case 1:
	{
		goto A
		A: // 这里编译没问题
	}
	case 2:
		goto B
		B: // syntax error: 跳转标签后缺少语句
	case 0:
		goto C
		C: // 这里编译没问题
	}
}
</code></pre>

<p>
</p>

编译错误信息表明跳转标签的声明之后必须跟一条语句。
但是，看上去，上例中的三个标签声明没什么不同，它们都没有跟随一条语句。
那为什么只有<code>B:</code>标签声明是不合法的呢？
原因是，根据上述第二条分号自动插入规则，编译器将在<code>A:</code>和<code>C:</code>标签声明之后的右大括号<code>}</code>字符之前插入一个分号，如下所示：

<pre class="line-numbers"><code class="language-go">func f(x int) {
	switch x {
	case 1:
	{
		goto A
		A:
	;} // 一个分号插入到了这里
	case 2:
		goto B
		B: // syntax error: 跳转标签后缺少语句
	case 0:
		goto C
		C:
	;} // 一个分号插入到了这里
}
</code></pre>

<p>
一个单独的分号实际上表示一条<a href="expressions-and-statements.html#simple-statements">空语句</a>。
这就意味着<code>A:</code>和<code>C:</code>标签声明之后确实跟随了一条语句，所以它们是合法的。
而<code>B:</code>标签声明跟随的<code>case 0:</code>不是一条语句，所以它是不合法的。
</p>

<p>
我们可以在<code>B:</code>标签声明之后手动插入一个分号使之变得合法。
</p>

</div>

<a class="anchor" id="commas"></a>
<h3>逗号<code>,</code>从不会被自动插入</h3>

<div>
<p>
一些包含多个类似项目的语法形式多用逗号<code>,</code>来做为这些项目之间的分割符，比如组合字面量和函数参数列表等。
在这样的一个语法形式中，最后一个项目后总可以跟一个可选的逗号。
如果此逗号为它所在代码行的最后一个有效字符，则此逗号是必需的；否则，此逗号可以省略。
编译器在任何情况下都不会自动插入逗号。
</p>

比如，下面的代码是合法的：

<pre class="line-numbers"><code class="language-go">func f1(a int, b string,) (x bool, y int,) {
	return true, 789
}
var f2 func (a int, b string) (x bool, y int)
var f3 func (a int, b string, // 最后一个逗号是必需的
) (x bool, y int,             // 最后一个逗号是必需的
)
var _ = []int{2, 3, 5, 7, 9,} // 最后一个逗号是可选的
var _ = []int{2, 3, 5, 7, 9,  // 最后一个逗号是必需的
}
var _ = []int{2, 3, 5, 7, 9}
var _, _ = f1(123, "Go",) // 最后一个逗号是可选的
var _, _ = f1(123, "Go",  // 最后一个逗号是必需的
)
var _, _ = f1(123, "Go")
</code></pre>

而下面这段代码是不合法的，因为编译器将自动在每一行的行尾插入一个分号（除了第二行）。
其中三行在插入分号后将导致编译错误。

<pre class="line-numbers"><code class="language-go">func f1(a int, b string,) (x bool, y int // error
) {
	return true, 789
}
var _ = []int{2, 3, 5, 7, 9 // error: unexpected newline
}
var _, _ = f1(123, "Go" // error: unexpected newline
)
</code></pre>

<p>
</p>

</div>

<h3>结束语</h3>

<p>
最后，根据上面的解释，在这里描述一下Go代码中的断行规则。
</p>

<div class="alert alert-success">

在Go代码中，以下断行是没问题的（不影响程序行为的）：
<ul>
<li>
	在除了<code>break</code>、<code>continue</code>和<code>return</code>这几个跳转关键字之外的任何关键字之后断行，或者在不跟随标签的<code>break</code>和<code>continue</code>关键字以及不跟随返回值的<code>return</code>关键字之后断行；
</li>
<li>
	在（显式输入的或者隐式被编译器插入的）分号<code>;</code>之后断行；
</li>
<li>
	在不会导致新的隐式分号被编译器插入的情况下断行。
</li>
</ul>
</div>

<p>
和很多Go中的其它设计细节一样，Go代码断行规则设计的评价也是褒贬不一。
有些程序员不太喜欢这样的断行规则，因为这样的规则限制了代码风格的自由度。
但是这些规则不但使得代码编译速度大大提高，另一方面也使得不同Go程序员写出的代码风格大体一致，从而相互可以比较轻松地读懂对方的代码。
</p>









